package com.pactera.madp.cp.common.util.sync;

import lombok.experimental.UtilityClass;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@UtilityClass
public class TreeUtil {

    public <T> List<T> buildTree(List<T> nodes, String idFieldName, String parentIdFieldName, String childListFieldName) throws Exception{
        List<T> trees = new ArrayList<>();
        for (T node : nodes) {
            boolean isSub = false;
            for (T comNode : nodes) {
                Object nodeId = getFieldValue(node, getFieldType(node, idFieldName), idFieldName);
                Object nodeParentId = getFieldValue(node, getFieldType(node, parentIdFieldName), parentIdFieldName);
                Object comNodeId = getFieldValue(comNode, getFieldType(comNode, idFieldName), idFieldName);
                if (compare(nodeId, comNodeId)) {
                    continue;
                }
                if (compare(nodeParentId, comNodeId)) {
                    Object childList = getFieldValue(comNode, getFieldType(comNode, childListFieldName), childListFieldName);
                    if (childList == null) {
                        setFieldValue(comNode, new ArrayList<>(), childListFieldName);
                    }
                    List<T> subList = (List<T>) getFieldValue(comNode, getFieldType(comNode, childListFieldName), childListFieldName);
                    subList.add(node);
                    isSub = true;
                    break;
                }
            }
            if (!isSub) {
                trees.add(node);
            }
        }
        return trees;
    }

    private <S> Class<?> getFieldType(S s, String fieldName) throws Exception{
        return s.getClass().getDeclaredField(fieldName).getType();

    }

    private <S, T> Object getFieldValue(S s, T t, String fieldName) throws Exception{
        Field field = s.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return (T) field.get(s);
    }

    private <S, T> void setFieldValue(S s, T t, String fieldName) throws Exception{
        Field field = s.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(s, t);
    }

    private boolean compare(Object nodeValue, Object comNodeValue) {
        return nodeValue.toString().equals(comNodeValue.toString());
    }
}
